
using System;
using System.Collections;
using System.Collections.Generic;
using ALPackage;
using UnityEngine;
using UnityEngine.UI;

namespace HYOK
{
    public static class UnityExtensions
    {
        public static float getHeight(this Text _text, string _value)
        {
            if (_text == null)
                return 0;
            
            TextGenerator tg = _text.cachedTextGeneratorForLayout;
            TextGenerationSettings ts = _text.GetGenerationSettings(_text.rectTransform.rect.size);
            return tg.GetPreferredHeight(_value, ts);
        }

        public static float getWidth(this Text _text, string _value)
        {
            if (_text == null)
                return 0;
            
            TextGenerator tg = _text.cachedTextGeneratorForLayout;
            TextGenerationSettings ts = _text.GetGenerationSettings(_text.rectTransform.rect.size);
            return tg.GetPreferredWidth(_value, ts);
        }

        public static string toDetailString(this Vector3 _v)
        {
            return $"({_v.x}, {_v.y}, {_v.z})";
        }
        
        public static string toDetailString(this Vector2 _v)
        {
            return $"({_v.x}, {_v.y})";
        }
        
        public static bool IsNaN(this Vector3 _v)
        {
            return float.IsNaN(_v.x) || float.IsNaN(_v.y) || float.IsNaN(_v.z);
        }
        
        /// <summary>
        /// 从一个 Transform 的所有子对象重找一个 Transform
        /// </summary>
        public static Transform findTransform(this Transform _root, Predicate<Transform> _predicate, bool ignoreSelf = true)
        {
            if (_root == null || _predicate == null)
                return null;

            if (!ignoreSelf && _predicate(_root))
                return _root;
            
            Queue<Transform> recursiveInsteadList = new Queue<Transform>(20);
            recursiveInsteadList.Enqueue(_root);
            while (recursiveInsteadList.Count > 0)
            {
                Transform parent = recursiveInsteadList.Dequeue();
                for (int i = 0; i < parent.childCount; i++)
                {
                    Transform childTrans = parent.GetChild(i);
                    if (_predicate(childTrans))
                        return childTrans;
                    recursiveInsteadList.Enqueue(childTrans);
                }
            }

            return null;
        }

        public static void doSomethingForAllChildren(this Transform _root, Action<Transform> _action, bool ignoreSelf = true)
        {
            if (_root == null || _action == null)
                return;

            if (!ignoreSelf) _action(_root);
            
            Queue<Transform> recursiveInsteadList = new Queue<Transform>(20);
            recursiveInsteadList.Enqueue(_root);
            while (recursiveInsteadList.Count > 0)
            {
                Transform parent = recursiveInsteadList.Dequeue();
                for (int i = 0; i < parent.childCount; i++)
                {
                    Transform childTrans = parent.GetChild(i);
                    _action(childTrans);
                    recursiveInsteadList.Enqueue(childTrans);
                }
            }
        } 

        /// <summary>
        /// 把本地坐标转换为另外一个 Transform 的本地坐标
        /// </summary>
        public static Vector3 transformPointToAnotherTransform(this Transform _transform, Transform _targetTransform, Vector3 _localPosition)
        {
            if (_transform == null || _targetTransform == null)
                return _localPosition;

            Vector4 homoPos = _targetTransform.worldToLocalMatrix * _transform.localToWorldMatrix * new Vector4(_localPosition.x, _localPosition.y, _localPosition.z, 1);
            return homoPos / homoPos.w;
        }

        /// <summary>
        /// 聚焦到 Content 里的某个本地坐标
        /// </summary>
        public static void focusToContentLocalPos(this ScrollRect _scrollRect, Vector3 _contentPos)
        {
            if (_scrollRect == null || _scrollRect.viewport == null || _scrollRect.content == null)
                return;

            Vector3 targetPos = _scrollRect.content.transformPointToAnotherTransform(_scrollRect.viewport, _contentPos);
            Vector3 currentPos = _scrollRect.viewport.rectPositionToLocalPosition(new Vector2(0.5f, 0.5f));
            _scrollRect.content.localPosition += currentPos - targetPos;
        }

        /// <summary>
        /// 把本地的 rectPosition 转换成本地坐标
        /// </summary>
        /// <remarks>
        /// 这里的 rectPosition 指的是 RectTransform 圈出的一个方形范围，左下角为 0，0，右上角为 1，1
        /// </remarks>
        public static Vector3 rectPositionToLocalPosition(this RectTransform _transform, Vector2 _rectPosition)
        {
            if (_transform == null)
                return Vector3.zero;

            Vector3 localScale = _transform.localScale;
            Rect localRect = _transform.rect;
            Vector2 size = new Vector2(
                localScale.x == 0 ? 0 : localRect.size.x / localScale.x,
                localScale.y == 0 ? 0 : localRect.size.y / localScale.y);

            Vector2 positionToCorner = _rectPosition * size;
            Vector2 pivotToCorner = _transform.pivot * size;
            return positionToCorner - pivotToCorner;
        }

        public static Rect getRectInParent(this RectTransform _rectTransform)
        {
            if (_rectTransform == null)
                return Rect.zero;
            
            if (_rectTransform.parent == null)
            {
                Rect worldRect = _rectTransform.rect;
                worldRect.center += (Vector2)_rectTransform.position;
                return worldRect;
            }

            Rect localRect = _rectTransform.rect;
            localRect.center += (Vector2)_rectTransform.localPosition;
            localRect.size = Vector2.Scale((Vector2)_rectTransform.localScale, localRect.size);
            return localRect;
        }

        /// <summary>
        /// 把改位置挪到目标位置下一个
        /// </summary>
        /// <param name="_rectTransform"></param>
        /// <param name="_targetRectTransform"></param>
        public static void SetSiblingNext(this RectTransform _rectTransform, RectTransform _targetRectTransform)
        {
            if(null == _targetRectTransform)
                return;
            
            _rectTransform.SetSiblingIndex(_targetRectTransform.GetSiblingIndex() + 1);
        }
        
        /// <summary>
        /// 简单的辅助方法，返回一个新的Vector，无副作用，用来减少代码行数
        /// </summary>
        /// <example>
        /// 例如
        /// Vector3 vec = transform.localPosition;
        /// vec.y = height;
        /// transform.localPosition = vec;
        /// 可以写成
        /// transform.localPosition = transform.localPosition.SetY(height);
        /// 
        /// 例如
        /// var dis = mob.transform.position - pos;
        /// dis.y = 0;
        /// float sqrDis = dis.sqrMagnitude;
        /// 可以写成
        /// float sqrDis = (mob.transform.position - pos).SetY(0).sqrMagnitude;
        /// </example>
        public static Vector3 SetX (this Vector3 vector, float x) {
            vector.x = x;
            return vector;
        }
        public static Vector3 SetY (this Vector3 vector, float y) {
            vector.y = y;
            return vector;
        }
        public static Vector3 SetZ (this Vector3 vector, float z) {
            vector.z = z;
            return vector;
        }
        public static Vector2 SetX (this Vector2 vector, float x) {
            vector.x = x;
            return vector;
        }
        public static Vector2 SetY (this Vector2 vector, float y) {
            vector.y = y;
            return vector;
        }

        #region Animation

        //采样
        public static void Sample(this Animation _anim, string _animName, float _normalizeTime)
        {
            if (_anim == null)
                return;

            AnimationState state = _anim[_animName];
            if (state == null)
                return;

            state.enabled = true;
            state.weight = 1f;
            state.normalizedTime = _normalizeTime;
            _anim.Sample();
            state.enabled = false;
            state.weight = 0f;
        }

        //强制重头播放
        public static void ForcePlay(this Animation _anim, string _animName, float _normalizedTime = 0f)
        {
            if (_anim == null)
                return;

            //解决重置动画以后播放不了的bug
            _anim.enabled = false;
            _anim.enabled = true;

            AnimationState state = _anim[_animName];
            if (state == null)
                return;

            state.normalizedTime = _normalizedTime;

            _anim.Play(_animName, PlayMode.StopAll);
        }
        
        //动画播放
        public static void Play(this Animation _anim, string _animationName, Action _complete)
        {
            if (_anim == null)
            {
                _complete?.Invoke();
                return;
            }

            //解决重置动画以后播放不了的bug
            _anim.enabled = false;
            _anim.enabled = true;

            AnimationClip clip = _anim.GetClip(_animationName);
            if (clip == null)
            {
                _complete?.Invoke();
                return;
            }

            _anim.Play(_animationName);
            if (_complete != null)
                ALCommonTaskController.CommonActionAddMonoTask(_complete, clip.length);
        }
        
        //获取动画时常
        public static float GetAnimationLength(this Animation _anim, string _animName)
        {
            if (_anim == null)
                return 0;

            AnimationClip clip = _anim.GetClip(_animName);
            if (clip == null)
                return 0;
            else
                return clip.length;
        }

        //设置动画播放速度
        public static void SetAnimSpeed(this Animation _anim, string _animName, float _speed)
        {
            if (_anim == null)
                return;

            if (!string.IsNullOrEmpty(_animName))
            {
                AnimationState state = _anim[_animName];
                if (state != null)
                    state.speed = _speed;
            }
        }

        //重制动画到0
        public static void ResetAnim(this Animation _anim, string _animName)
        {
            if (_anim == null)
                return;

            if (!string.IsNullOrEmpty(_animName))
            {
                AnimationState state = _anim[_animName];
                if (state == null)
                    return;

                _anim.Play(_animName);
                state.time = 0;
                state.enabled = true;
                _anim.Sample(_animName, 0);
                state.enabled = false;
            }
        }
        
        /// <summary>
        /// 主要用于播放不受timeScale影响的animation
        /// </summary>
        /// <param name="animation"></param>
        /// <param name="clipName"></param>
        /// <param name="useTimeScale"></param>
        /// <param name="onComplete"></param>
        /// <returns></returns>
        public static IEnumerator Play( this Animation animation, string clipName, bool useTimeScale, Action onComplete )
        {
            //We Don't want to use timeScale, so we have to animate by frame..
            if(!useTimeScale)
            {
                AnimationState _currState = animation[clipName];
                bool isPlaying = true;
                float _startTime = 0F;
                float _progressTime = 0F;
                float _timeAtLastFrame = 0F;
                float _timeAtCurrentFrame = 0F;
                float deltaTime = 0F;
  
  
                animation.Play(clipName);
  
                _timeAtLastFrame = Time.realtimeSinceStartup;
                while (isPlaying) 
                {
                    _timeAtCurrentFrame = Time.realtimeSinceStartup;
                    deltaTime = _timeAtCurrentFrame - _timeAtLastFrame;
                    _timeAtLastFrame = _timeAtCurrentFrame; 
  
                    _progressTime += deltaTime;
                    if(_currState.length > 0)
                        _currState.normalizedTime = _progressTime / _currState.length; 
                    animation.Sample ();
  
                    //Debug.Log(_progressTime);
  
                    if (_progressTime >= _currState.length) 
                    {
                        //Debug.Log(&quot;Bam! Done animating&quot;);
                        if(_currState.wrapMode != WrapMode.Loop)
                        {
                            //Debug.Log(&quot;Animation is not a loop anim, kill it.&quot;);
                            //_currState.enabled = false;
                            isPlaying = false;
                        }
                        else
                        {
                            //Debug.Log(&quot;Loop anim, continue.&quot;);
                            _progressTime = 0.0f;
                        }
                    }
  
                    yield return new WaitForEndOfFrame();
                }
                yield return null;
                if(onComplete != null)
                {
                    onComplete();
                } 
            }
            else
            {
                animation.Play(clipName);
            }
        }
        #endregion
    }
}